﻿using System;
using System.Collections.Generic;
using TFramework.ECS.Collection;
using TFramework.ECS.Entity;
using TFramework.ECS.Group;
using TFramework.ECS.Group.Observable;
using TFramework.Extensions;
using TFramework.Extensions.VContainer;
using UniRx;
using VContainer.Unity;

namespace TFramework.ECS.System.Handler
{
    public class UpdateSystemHandler : ISystemHandler
    {
        public readonly IEntityCollection EntityCollectionManager;
        public readonly IDictionary<IUpdateSystem, Subject<IUpdateSystem>> _updateSystems;
        public readonly IDictionary<ISystem, IDisposable> _systemSubscriptions;
        private bool isDisposed;

        public UpdateSystemHandler(IEntityCollection entityCollectionManager)
        {
            EntityCollectionManager = entityCollectionManager;
            _updateSystems = new Dictionary<IUpdateSystem, Subject<IUpdateSystem>>();
            _systemSubscriptions = new Dictionary<ISystem, IDisposable>();
        }

        public bool CanHandleSystem(ISystem system)
        {
            { return system is IUpdateSystem; }
        }

        public void SetupSystem(ISystem system)
        {
            var entitySubscriptions = new Dictionary<int, IDisposable>();
            var entityChangeSubscriptions = new CompositeDisposable();
            _systemSubscriptions.Add(system, entityChangeSubscriptions);

            var castSystem = (IUpdateSystem)system;
            var observableGroup = EntityCollectionManager.GetObservableGroup(system.Group);

            observableGroup.OnEntityAdded
                .Subscribe(x => { Update(castSystem, x, entityChangeSubscriptions, observableGroup); })
                .AddTo(entityChangeSubscriptions);
        }

        private void Update(IUpdateSystem system, IEntity entity, CompositeDisposable entityChangeSubscriptions, IObservableGroup observableGroup)
        {
            Subject<IUpdateSystem> updateSystemSubject;
            if (_updateSystems.ContainsKey(system))
            {
                updateSystemSubject = _updateSystems[system];
            }
            else
            {
                updateSystemSubject = new Subject<IUpdateSystem>();
                _updateSystems.Add(system, updateSystemSubject);
            }

            var disposable = updateSystemSubject.Subscribe(x => { x.Update(entity); });

            observableGroup.OnEntityRemoving
                .Subscribe(x => { disposable.Dispose(); })
                .AddTo(entityChangeSubscriptions);
        }

        public void DestroySystem(ISystem system)
        {
            if (isDisposed)
                return;
            _systemSubscriptions.RemoveAndDispose(system);
        }

        public void Dispose()
        {
            isDisposed = true;
            _systemSubscriptions.DisposeAll();
        }
    }
}
